package com.xyzwps.paimon.furina.modules.auth.app

import com.xyzwps.paimon.furina.common.AppErrorCode.*
import com.xyzwps.paimon.furina.common.AppException
import com.xyzwps.paimon.furina.common.value.Phone
import com.xyzwps.paimon.furina.modules.auth.service.JwtService
import com.xyzwps.paimon.furina.modules.auth.service.UserAccountService
import com.xyzwps.paimon.furina.modules.auth.service.UserPasswordService
import com.xyzwps.paimon.furina.modules.sms.vcode.VcodeScene
import com.xyzwps.paimon.furina.modules.sms.vcode.VcodeService
import com.xyzwps.paimon.furina.modules.me.MeApplication
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.transactions.transaction
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service

@Service
class AuthApplication(
    private val userAccountService: UserAccountService,
    private val userPasswordService: UserPasswordService,
    private val meApp: MeApplication,
    private val passwordEncoder: PasswordEncoder,
    private val vcodeService: VcodeService,
    private val jwtService: JwtService,
    private val db: Database,
) {

    fun register(payload: RegisterPayload) {
        val phone = payload.phone
        vcodeService.checkVcode(phone, VcodeScene.REGISTER, payload.vcode)

        val encryptedPassword = passwordEncoder.encode(payload.password)
        transaction(db) {
            userAccountService.register(phone, encryptedPassword)
        }
    }

    fun registerVcode(phone: Phone) = vcodeService.sendVcode(phone, VcodeScene.REGISTER)

    fun login(payload: LoginPayload): LoginResult {
        val phone = payload.phone
        vcodeService.checkVcode(phone, VcodeScene.LOGIN, payload.vcode)

        val userAccount = transaction(db) {
            val userAccount = userAccountService.findByPhone(phone)
                ?: throw AppException(LOGIN_INFO_INVALID, "用户名或密码错误")
            val userPassword = userPasswordService.findByUserId(userAccount.id)
                ?: throw AppException(USER_HAS_NO_PASSWORD, "未设置密码")

            if (!passwordEncoder.matches(payload.password, userPassword.encryptedPassword)) {
                throw AppException(LOGIN_INFO_INVALID, "用户名或密码错误")
            }
            if (userAccount.locked) {
                throw AppException(ACCOUNT_LOCKED, "账户已被锁定")
            }
            if (userAccount.disabled) {
                throw AppException(ACCOUNT_DISABLED, "账户已被禁用")
            }

            return@transaction userAccount
        }

        val jwt = jwtService.createJwt(userId = userAccount.id)
        return LoginResult(meApp.getRichMeInfo(userAccount), jwt)
    }

    fun loginVcode(phone: Phone) = vcodeService.sendVcode(phone, VcodeScene.LOGIN)
}